package com.ziyucoding.springboot.labs.lab10.seatdemo.tccscfeign.orderservice.service.impl;

import com.ziyucoding.springboot.labs.lab10.seatdemo.tccscfeign.orderservice.dao.OrderDao;
import com.ziyucoding.springboot.labs.lab10.seatdemo.tccscfeign.orderservice.dataobject.OrderDO;
import com.ziyucoding.springboot.labs.lab10.seatdemo.tccscfeign.orderservice.feign.ProductServiceFeignClient;
import com.ziyucoding.springboot.labs.lab10.seatdemo.tccscfeign.orderservice.feign.TransactionalRecordServiceFeignClient;
import com.ziyucoding.springboot.labs.lab10.seatdemo.tccscfeign.orderservice.feign.dto.ProductFrozenStockDTO;
import com.ziyucoding.springboot.labs.lab10.seatdemo.tccscfeign.orderservice.feign.dto.TransactionalRecordSaveDTO;
import com.ziyucoding.springboot.labs.lab10.seatdemo.tccscfeign.orderservice.service.OrderTccService;
import com.ziyucoding.springboot.labs.lab10.seatdemo.tccscfeign.orderservice.utils.IdempotentUtil;
import io.seata.rm.tcc.api.BusinessActionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;
import java.util.Objects;

/**
 * 订单服务实现的TCC事务模式
 *
 * @author ziyucoding
 * @date 2022-11-06
 **/
@Service
public class OrderTccServiceImpl implements OrderTccService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private ProductServiceFeignClient productService;

    @Autowired
    private TransactionalRecordServiceFeignClient transactionalRecordService;

    /**
     * TCC的第一阶段try，@Transactional该注解开启了本地事务，try阶段的全部事务将会回滚
     *
     * @param businessActionContext
     * @param userId
     * @param orderId
     * @param productId
     * @param price
     * @param amount
     * @return boolean
     **/
    @Override
    @Transactional
    public boolean tryCreate(BusinessActionContext businessActionContext, Long userId, Integer orderId, Long productId, Integer price, Integer amount) {
        logger.info("--------开始第一阶段的事务，事务组XID：{}---------", businessActionContext.getXid());
        String xid = businessActionContext.getXid();
        //判断cancel阶段是否已经执行过--事务日志
        Map<String, Object> trMap = transactionalRecordService.findTransactionalRecordByXid(xid);
        Map<String, Object> trDataMap = (Map<String, Object>) trMap.get("data");
        Map<String, Object> trDataItemMap = (Map<String, Object>) trDataMap.get("item");
        Boolean isSuccess = (Boolean) trDataMap.get("isSuccess");
        if (isSuccess == false) {
            throw new RuntimeException("事务日志查询失败！");
        }

        //1：try  2：commit 3：cancel
        if (Objects.nonNull(trDataItemMap) && Integer.valueOf(3).equals(trDataItemMap.get("status")))
            throw new RuntimeException("已经进入了TCC第二阶段的cancel阶段，不允许try阶段！");

        //冻结库存
        ProductFrozenStockDTO productFrozenStockDTO = new ProductFrozenStockDTO();
        productFrozenStockDTO.setProductId(productId);
        productFrozenStockDTO.setAmount(amount);
        Map<String, Object> pfsMap = productService.frozenStock(productFrozenStockDTO);
        isSuccess = (Boolean) pfsMap.get("data");
        if (isSuccess == false) {
            throw new RuntimeException("冻结库存失败！");
        }

        //生成订单
        OrderDO orderDO = new OrderDO();
        orderDO.setId(orderId);
        orderDO.setProductId(productId);
        orderDO.setPayAmount(amount * price);
        orderDO.setUserId(userId);
        orderDO.setStatus(5); // 待确认
        orderDao.saveOrder(orderDO);
        logger.info("[tryCreate] 生成订单: {}", orderDO.getId());

        //保证幂等性
        IdempotentUtil.add(getClass(), xid, System.currentTimeMillis());

        return false;
    }

    @Override
    @Transactional
    public boolean commit(BusinessActionContext businessActionContext) {
        String xid = businessActionContext.getXid();
        //校验幂等性，防止多次提交
        if (Objects.isNull(IdempotentUtil.get(getClass(), xid))) {
            return true;
        }

        logger.info("--------开始第二阶段的commit事务，事务组XID：{}---------", xid);
        long productId = Long.parseLong(businessActionContext.getActionContext("productId").toString());
        int amount = Integer.parseInt(businessActionContext.getActionContext("amount").toString());
        int orderId = Integer.parseInt(businessActionContext.getActionContext("orderId").toString());

        //释放冻结的库存
        ProductFrozenStockDTO productFrozenStockDTO = new ProductFrozenStockDTO();
        productFrozenStockDTO.setProductId(productId);
        productFrozenStockDTO.setAmount(amount);
        Map<String, Object> pfsMap = productService.cleanFrozenStock(productFrozenStockDTO);
        Boolean isSuccess = (Boolean) pfsMap.get("data");
        if (isSuccess == false) {
            return false;
        }

        //修改订单的状态为已完成
        OrderDO orderDO = new OrderDO();
        orderDO.setId(orderId);
        orderDO.setStatus(3); //已完成状态
        orderDao.updateOrderStatus(orderDO);
        logger.info("[commit] 修改状态，订单: {}，状态: {}", orderDO.getId(), orderDO.getStatus());
        //提交成功，则移出
        IdempotentUtil.remove(getClass(), xid);
        return false;
    }

    @Override
    @Transactional
    public boolean rollback(BusinessActionContext businessActionContext) {
        String xid = businessActionContext.getXid();
        //防止悬挂，只要cancel插入一条事务记录
        TransactionalRecordSaveDTO trsDTO = new TransactionalRecordSaveDTO();
        trsDTO.setXid(xid);
        trsDTO.setStatus(2);  //cancel阶段
        Map<String, Object> trMap = transactionalRecordService.saveTransactionalRecord(trsDTO);
        Boolean isSuccess = (Boolean) trMap.get("data");
        if (isSuccess == false) {
            return false;
        }

        //校验幂等性、空回滚
        if (Objects.isNull(IdempotentUtil.get(getClass(), xid))) {
            return true;
        }

        logger.info("--------开始第二阶段的rollback事务，事务组XID：{}---------", xid);
        long productId = Long.parseLong(businessActionContext.getActionContext("productId").toString());
        int amount = Integer.parseInt(businessActionContext.getActionContext("amount").toString());
        int orderId = Integer.parseInt(businessActionContext.getActionContext("orderId").toString());
        //恢复冻结的库存
        ProductFrozenStockDTO productFrozenStockDTO = new ProductFrozenStockDTO();
        productFrozenStockDTO.setProductId(productId);
        productFrozenStockDTO.setAmount(amount);
        Map<String, Object> pfsMap = productService.rollbackFrozenStock(productFrozenStockDTO);
        isSuccess = (Boolean) pfsMap.get("data");
        if (isSuccess == false) {
            return false;
        }

        //删除该笔订单--逻辑删除
        OrderDO orderDO = new OrderDO();
        orderDO.setId(orderId);
        orderDO.setStatus(5); //删除
        orderDao.updateOrderStatus(orderDO);
        logger.info("[rollback] 修改状态，订单: {}，状态: {}", orderDO.getId(), orderDO.getStatus());

        //回滚成功，则移出
        IdempotentUtil.remove(getClass(), xid);
        return false;
    }
}
